#pragma once
#include <brpc/channel.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <mutex>

#include "logger.hpp"

namespace im{
    ///
    /// @brief 单个服务信道类
    /// @details 封装单个服务的信道管理类
    /// 
    class ServiceChannel{
    public:
        using ptr = std::shared_ptr<ServiceChannel>;
        using ChannelPtr = std::shared_ptr<brpc::Channel>;

        ServiceChannel(const std::string& name)
            :_index(0), _service_name(name){}

        void append(const std::string& host){
            brpc::ChannelOptions opt;
            opt.connect_timeout_ms = -1;
            ChannelPtr channel = std::make_shared<brpc::Channel>();
            if (-1 == channel->Init(host.c_str(), &opt)) {
                LOG_CRITICAL("初始化{}-{}信道失败!", _service_name, host);
                return;
            }

            std::unique_lock<std::mutex> lock(_mutex);
            _channels.push_back(channel);
            _hosts.insert(std::make_pair(host, channel));
        }

        void remove(const std::string& host){
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _hosts.find(host);
            if(it == _hosts.end()){
                LOG_WARN("{}-{}节点删除信道时，没有找到信道信息！", _service_name, host);
                return ;
            }
            for(auto vit = _channels.begin(); vit != _channels.end(); ++vit)
                if(*vit == it->second){
                    _channels.erase(vit);
                    break;
                }
            _hosts.erase(it); // it放后面删，不然it会失效
        }

        ChannelPtr choose(){
            std::unique_lock<std::mutex> lock(_mutex);
            if(_channels.size() == 0){
                LOG_CRITICAL("当前没有能够提供 {} 服务的节点！", _service_name);
                return ChannelPtr();
            }
            int idx = _index++ % _channels.size();
            return _channels[idx];
        }

    private:
        int32_t _index; //当前轮转下标计数器
        std::string _service_name;//服务名称
        std::mutex _mutex;
        std::vector<ChannelPtr> _channels;  // 当前服务对应的信道
        std::unordered_map<std::string, ChannelPtr> _hosts; // 主机地址和信道的映射
    };

    ///
    /// @brief 服务信道管理类
    /// @details 总体的服务信道管理类
    /// 
    class ServiceManager{
    public:
        using ptr = std::shared_ptr<ServiceManager>;
        ServiceManager() {}
        // 获取指定服务的节点信道
        ServiceChannel::ChannelPtr choose(const std::string& ServiceName){
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _services.find(ServiceName);
            if(it == _services.end()){
                LOG_CRITICAL("当前没有能够提供 {} 服务的节点！", ServiceName);
                return nullptr;
            }
            return it->second->choose();
        }
        // 先声明，我关注哪些服务的上下线，不关心的就不需要管理了
        void declared(const std::string& ServiceName){
            std::unique_lock<std::mutex> lock(_mtx);
            _follow_services.insert(ServiceName);
        }
        // 服务上线时调用的回调接口，将服务节点管理起来
        void onServiceOnline(const std::string& serviceInstance, const std::string& host){
            std::string serviceName = getServiceName(serviceInstance);
            ServiceChannel::ptr service;
            {
                std::unique_lock<std::mutex> lock(_mtx);
                auto iter = _follow_services.find(serviceName);
                if(iter == _follow_services.end()){
                    LOG_INFO("{}-{} 服务上线了，但是当前并不关心！", serviceName, host);
                    return;
                }

                auto iter_serivce = _services.find(serviceName);
                if(iter_serivce == _services.end()){
                    service = std::make_shared<ServiceChannel>(serviceName);
                    _services.insert(std::make_pair(serviceName, service));
                }else{
                    service = iter_serivce->second;
                }
            }
            if (!service) {
                LOG_CRITICAL("新增 {} 服务管理节点失败！", serviceName);
                return ;
            }
            service->append(host);
            LOG_DEBUG("{}-{} 服务上线新节点，已进行添加管理！", serviceName, host);

        }
        // 服务下线时调用的回调接口，从服务信道管理中，删除指定节点信道
        void onServiceOffline(const std::string &serviceInstance, const std::string &host){
            std::string serviceName = getServiceName(serviceInstance);
            ServiceChannel::ptr service;
            {
                std::unique_lock<std::mutex> lock(_mtx);
                auto fit = _follow_services.find(serviceName);
                if (fit == _follow_services.end()) {
                    LOG_DEBUG("{}-{} 服务下线了，但是当前并不关心！", serviceName, host);
                    return;
                }
                //先获取管理对象，没有则创建，有则添加节点
                auto sit = _services.find(serviceName);
                if (sit == _services.end()) {
                    LOG_WARN("删除{}服务节点时，没有找到管理对象", serviceName);
                    return;
                }
                service = sit->second;
            }
            service->remove(host);
            LOG_DEBUG("{}-{} 服务下线节点，进行删除管理！", serviceName, host);
        }

    private:
        std::string getServiceName(const std::string &service_instance) {
            auto pos = service_instance.find_last_of('/');
            if (pos == std::string::npos) return service_instance;
            return service_instance.substr(0, pos);
        }

    private:
        std::mutex _mtx;
        std::unordered_set<std::string> _follow_services;    // 关注的服务
        std::unordered_map<std::string, ServiceChannel::ptr> _services;
    };
}